package com.xhq.netty.io;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

public class NoBlockIO {


    public static void NIO() throws Exception {
        var server = ServerSocketChannel.open();
        server.bind(new InetSocketAddress(9090));
        server.configureBlocking(false); // 非阻塞， NIO， 这里也可以配置成阻塞
        System.out.println("server start at 9090");

        LinkedList<SocketChannel> clients = new LinkedList<>();
        new Thread(() ->{
            readMultiConnectionMessageUseOneThread(clients);
        }).start();

        while (true){
            var client = server.accept();
            if(client != null){
                client.configureBlocking(false);
                System.out.println("accept client " + client.socket().getPort());
                clients.add(client);
            }
            TimeUnit.MILLISECONDS.sleep(100);
        }
    }

    private static void readMultiConnectionMessageUseOneThread(LinkedList<SocketChannel> clients){
        var buffer = ByteBuffer.allocate(2048);
        System.out.println("ByteBuffer 初始时的 limit 值是：" + buffer.limit());
        while (true){
            var isEmpty = true;
            if(!clients.isEmpty()){
                var closedClients = new ArrayList<SocketChannel>();
                for(var c : clients){
                    try {
                        var num = c.read(buffer);
                        if(num < 0){
                            try {
                                c.close();
                            } catch (IOException ioException) {
                                ioException.printStackTrace();
                            }
                            closedClients.add(c);
                            isEmpty = false;
                            System.out.println("client " + c.socket().getPort() + " off.");
                        } else if(num > 0){
                            readFromBufferAndPrint(buffer, c);
                            replyAndPrint(buffer, c);
                            isEmpty = false;
                        }
                    } catch (IOException e) {
                        try {
                            c.close();
                        } catch (IOException ioException) {
                            ioException.printStackTrace();
                        }
                        closedClients.add(c);
                        isEmpty = false;
                        System.out.println("read from client " + c.socket().getPort() + " occur exception, may be off.");
                    }
                }

                if(!closedClients.isEmpty()){
                    closedClients.forEach(clients::remove);
                }
            }

            if(isEmpty){
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void readFromBufferAndPrint(ByteBuffer buffer, SocketChannel client){
        buffer.flip();
        var length = buffer.limit();
        byte[] data = new byte[length];
        buffer.get(data);
        String dataStr = new String(data);
        System.out.println("port " + client.socket().getPort() + "(" + length + "B):" + dataStr);
        buffer.clear();
    }

    private static void replyAndPrint(ByteBuffer buffer, SocketChannel client){
        buffer.clear();
        var data = "server had receive your message".getBytes();
        buffer.put(data);
        try {
            buffer.flip();
            client.write(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
        buffer.clear();
    }

}
